/*
*  Copyright 2019-2020 Zheng Jie
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package me.zhengjie.modules.zhaojiu.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.system.domain.Dept;
import me.zhengjie.modules.system.service.dto.DeptDto;
import me.zhengjie.modules.zhaojiu.domain.Department;
import me.zhengjie.utils.*;
import lombok.RequiredArgsConstructor;
import me.zhengjie.modules.zhaojiu.repository.DepartmentRepository;
import me.zhengjie.modules.zhaojiu.service.DepartmentService;
import me.zhengjie.modules.zhaojiu.service.dto.DepartmentDto;
import me.zhengjie.modules.zhaojiu.service.dto.DepartmentQueryCriteria;
import me.zhengjie.modules.zhaojiu.service.mapstruct.DepartmentMapper;
import me.zhengjie.utils.enums.DataScopeEnum;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Field;
import java.util.*;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;
import java.util.stream.Collectors;

/**
* @website https://el-admin.vip
* @description 服务实现
* @author Jiayin Wei
* @date 2021-12-09
**/
@Service
@RequiredArgsConstructor
@CacheConfig(cacheNames = "department")
public class DepartmentServiceImpl implements DepartmentService {

    private final DepartmentRepository departmentRepository;
    private final DepartmentMapper departmentMapper;
    private final RedisUtils redisUtils;

    @Override
    public List<DepartmentDto> queryAll(DepartmentQueryCriteria criteria, Boolean isQuery) throws Exception{
        Sort sort = Sort.by(Sort.Direction.ASC, "deptSort");
        String dataScopeType = SecurityUtils.getDataScopeType();
        if (isQuery) {
            if(dataScopeType.equals(DataScopeEnum.ALL.getValue())){
                criteria.setPidIsNull(true);
            }
            List<Field> fields = QueryHelp.getAllFields(criteria.getClass(), new ArrayList<>());
            List<String> fieldNames = new ArrayList<String>(){{ add("pidIsNull");add("enabled");}};
            for (Field field : fields) {
                //设置对象的访问权限，保证对private的属性的访问
                field.setAccessible(true);
                Object val = field.get(criteria);
                if(fieldNames.contains(field.getName())){
                    continue;
                }
                if (ObjectUtil.isNotNull(val)) {
                    criteria.setPidIsNull(null);
                    break;
                }
            }
        }
        List<DepartmentDto> list = departmentMapper.toDto(departmentRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),sort));
        // 如果为空，就代表为自定义权限或者本级权限，就需要去重，不理解可以注释掉，看查询结果
        if(StringUtils.isBlank(dataScopeType)){
            return deduplication(list);
        }
        return list;


        //Page<Department> page = departmentRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable);
        //return PageUtil.toPage(page.map(departmentMapper::toDto));
    }

    @Override
    @Cacheable(key = "'deptId:' + #p0")
    public DepartmentDto findById(Long deptId) {
        Department department = departmentRepository.findById(deptId).orElseGet(Department::new);
        ValidationUtil.isNull(department.getDeptId(),"Department","deptId", deptId);
        return departmentMapper.toDto(department);
    }

    @Override
    public List<Department> findByPid(long pid) {
        return departmentRepository.findByPid(pid);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(Department resources) {
        departmentRepository.save(resources);
        // 计算子节点数目
        resources.setSubCount(0);
        // 清理缓存
        updateSubCnt(resources.getPid());
        // 清理datascope缓存
        delCaches(resources.getPid());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(Department resources) {
        // 旧的部门
        Long oldPid = findById(resources.getDeptId()).getPid();
        Long newPid = resources.getPid();
        if(resources.getPid() != null && resources.getDeptId().equals(resources.getPid())) {
            throw new BadRequestException("上级不能为自己");
        }
        Department department = departmentRepository.findById(resources.getDeptId()).orElseGet(Department::new);
        ValidationUtil.isNull( department.getDeptId(),"Department","deptId",resources.getDeptId());
        resources.setDeptId(department.getDeptId());
        departmentRepository.save(resources);
        // 更新父节点中子节点数目
        updateSubCnt(oldPid);
        updateSubCnt(newPid);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Set<DepartmentDto> departmentDtos) {
        for (DepartmentDto departmentDto : departmentDtos) {
            // 清理缓存
            delCaches(departmentDto.getDeptId());
            departmentRepository.deleteById(departmentDto.getDeptId());
            updateSubCnt(departmentDto.getPid());
        }
    }

    @Override
    public void download(List<DepartmentDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (DepartmentDto department : all) {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("上级部门", department.getPid());
            map.put("年份", department.getYear());
            map.put("类型", department.getType());
            map.put("子部门数目", department.getSubCount());
            map.put("名称", department.getName());
            map.put("排序", department.getDeptSort());
            map.put("状态", department.getEnabled());
            map.put("创建者", department.getCreateBy());
            map.put("更新者", department.getUpdateBy());
            map.put("创建日期", department.getCreateTime());
            map.put("更新时间", department.getUpdateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    public Set<DepartmentDto> getDeleteDepartments(List<Department> menuList, Set<DepartmentDto> departmentDtos) {
        for (Department department : menuList) {
            departmentDtos.add(departmentMapper.toDto(department));
            List<Department> departments = departmentRepository.findByPid(department.getDeptId());
            if(departments!=null && departments.size()!=0){
                getDeleteDepartments(departments, departmentDtos);
            }
        }
        return departmentDtos;
    }

    @Override
    public List<Long> getDepartmentChildren(List<Department> departmentList) {
        List<Long> list = new ArrayList<>();
        departmentList.forEach(department -> {
                if (department!=null && department.getEnabled()) {
                    List<Department> departments = departmentRepository.findByPid(department.getDeptId());
                    if (departments.size() != 0) {
                        list.addAll(getDepartmentChildren(departments));
                    }
                    list.add(department.getDeptId());
                }
            }
        );
        return list;
    }

    @Override
    public List<DepartmentDto> getSuperior(DepartmentDto departmentDto, List<Department> depts) {
        if(departmentDto.getPid() == null){
            depts.addAll(departmentRepository.findByPidIsNull());
            return departmentMapper.toDto(depts);
        }
        depts.addAll(departmentRepository.findByPid(departmentDto.getPid()));
        return getSuperior(findById(departmentDto.getPid()), depts);
    }

    @Override
    public Object buildTree(List<DepartmentDto> departmentDtos) {
        Set<DepartmentDto> trees = new LinkedHashSet<>();
        Set<DepartmentDto> departments= new LinkedHashSet<>();
        List<String> departmentNames = departmentDtos.stream().map(DepartmentDto::getName).collect(Collectors.toList());
        boolean isChild;
        for (DepartmentDto departmentDTO : departmentDtos) {
            isChild = false;
            if (departmentDTO.getPid() == null) {
                trees.add(departmentDTO);
            }
            for (DepartmentDto it : departmentDtos) {
                if (it.getPid() != null && departmentDTO.getDeptId().equals(it.getPid())) {
                    isChild = true;
                    if (departmentDTO.getChildren() == null) {
                        departmentDTO.setChildren(new ArrayList<>());
                    }
                    departmentDTO.getChildren().add(it);
                }
            }
            if(isChild) {
                departments.add(departmentDTO);
            } else if(departmentDTO.getPid() != null &&  !departmentNames.contains(findById(departmentDTO.getPid()).getName())) {
                departments.add(departmentDTO);
            }
        }

        if (CollectionUtil.isEmpty(trees)) {
            trees = departments;
        }
        Map<String,Object> map = new HashMap<>(2);
        map.put("totalElements",departmentDtos.size());
        map.put("content",CollectionUtil.isEmpty(trees)? departmentDtos :trees);
        return map;
    }

    private void updateSubCnt(Long deptId){
        if(deptId != null){
            int count = departmentRepository.countByPid(deptId);
            departmentRepository.updateSubCntById(count, deptId);
        }
    }

    private List<DepartmentDto> deduplication(List<DepartmentDto> list) {
        List<DepartmentDto> deptDtos = new ArrayList<>();
        for (DepartmentDto deptDto : list) {
            boolean flag = true;
            for (DepartmentDto dto : list) {
                if (dto.getDeptId().equals(deptDto.getPid())) {
                    flag = false;
                    break;
                }
            }
            if (flag){
                deptDtos.add(deptDto);
            }
        }
        return deptDtos;
    }

    /**
     * 清理缓存
     * @param id /
     */
    public void delCaches(Long id){
        redisUtils.del(CacheKey.DEPT_ID + id);
    }
}
